import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

/**
 * Handles user interaction with listeners. Calls view and model as needed.
 * 
 * @author Vo, Wu and Wyan
 * 
 */

public class BattleshipController {
	/**
	 * The model that is being displayed.
	 */
	private BattleshipModel bModel;

	/**
	 * The view that is being displayed.
	 */
	private BattleshipView bView;

	/**
	 * Boolean value to indicate if both player's ships have been placed.
	 */
	private Boolean shipsPlaced = false;

	/**
	 * Boolean value to flag that it is player 1 turn.
	 */
	private Boolean p1Turn = true;

	/**
	 * Boolean to get the starting point for each ships that are being placed.
	 */
	private Boolean startPointPlacement = true;

	/**
	 * Boolean value that indicates whether or not the game is over.
	 */
	private boolean gameOver = false;

	/**
	 * Integer that indicates which ship is being added.
	 */
	private int shipCount;

	/**
	 * The starting cell location of the current ship being placed.
	 */
	private String currentStart;

	/**
	 * An array that collects the cells that are occupied by ship.
	 */
	private List<String> endpoints;

	/**
	 * Holds the max value that a ship index can be.
	 */
	private static final int MAX_SHIP_INDEX = 4;

	/**
	 * Producing battle ship model and view.
	 * 
	 * @param model
	 * <br />
	 *            the type of the ship
	 * @param view
	 * <br />
	 *            the length that comes if each type of the ship
	 * @requires only use the specific type of ships that are allowed to be in
	 *          the game and the length
	 * @ensures the name of the ship is the correct type of the ship and the
	 *         length of the ship is correct
	 * 
	 */
	public BattleshipController(BattleshipModel model, BattleshipView view) {
		bModel = model;
		bView = view;

		// Initialize view
		bView.enableGrid(true);
		bView.disableGrid(false);

		// Add listeners to view
		bView.addActionListener(new PlacementListener());
		bView.addActionListener(new AttackListener());

	}

	/**
	 * The inner class AttackListener registers clicks during the attack phase
	 * of the game and calls bModel to interpret and change the game data. These
	 * changes are sent back to the controller and the controller instructs the
	 * view to update appropriately.
	 */
	private class AttackListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			if (shipsPlaced) {
				String clickedCell = e.getActionCommand();

				if (!gameOver) {
					if (p1Turn) {
						String result = bModel.p1Attack(clickedCell);
						if (result.equals("INVALID")) {
							bView.updateLabel("Pick another cell");
						} else {
							if (result.equals("HIT")) {
								bView.updateGrid(p1Turn, clickedCell, "HIT");
								bView.updateLabel("Hit!");
							} else if (result.equals("MISS")) {
								bView.updateGrid(p1Turn, clickedCell, "MISS");
								bView.updateLabel("Miss!");

							} else {
								List<String> sunkCells = bModel.getSunkCells(
										p1Turn, result);
								bView.updateGrid(p1Turn, clickedCell, "SUNK");
								bView.updateLabel("Player 1 has sunk Player 2's "
										+ result + "!");

								// Color all sunk cells
								for (String s : sunkCells) {
									bView.updateGrid(p1Turn, s, "SUNK");
								}

								if (bModel.isGameOver(p1Turn)) {
									List<String> remaining = bModel
											.getSurvivingCells(p1Turn);
									gameOver = true;
									bView.updateLabel("Game Over! Player 1 Wins!");

									// Show remaining ships
									for (String s : remaining) {
										bView.updateGrid(!p1Turn, s,
												"REMAINING");
									}

									bView.disableGrid(true);
									bView.disableGrid(false);
								}
							}
							changeTurns();
						}

					} else {
						String result = bModel.p2Attack(clickedCell);
						if (result.equals("INVALID")) {
							bView.updateLabel("Pick another cell");

						} else {
							if (result.equals("HIT")) {
								bView.updateGrid(p1Turn, clickedCell, "HIT");
								bView.updateLabel("Hit!");
							} else if (result.equals("MISS")) {
								bView.updateGrid(p1Turn, clickedCell, "MISS");
								bView.updateLabel("Miss!");
							} else {
								List<String> sunkCells = bModel.getSunkCells(
										p1Turn, result);
								bView.updateGrid(p1Turn, clickedCell, "SUNK");
								bView.updateLabel("Player 2 has sunk Player 1's "
										+ result + "!");

								// Color all sunk cells
								for (String s : sunkCells) {
									bView.updateGrid(p1Turn, s, "SUNK");
								}

								if (bModel.isGameOver(p1Turn)) {
									List<String> remaining = bModel
											.getSurvivingCells(p1Turn);
									gameOver = true;
									bView.updateLabel("Game Over! Player 2 Wins!");

									// Show remaining ships
									for (String s : remaining) {
										bView.updateGrid(!p1Turn, s,
												"REMAINING");
									}

									bView.disableGrid(true);
									bView.disableGrid(false);
								}
							}
							changeTurns();
						}
					}
				}

			}
		}
	}

	/**
	 * The inner class PlacementListener registers the cells that the user
	 * clicks and determines the valid end points. Once a valid end point, the
	 * cells are added to the ships in bModel
	 */
	private class PlacementListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			if (!shipsPlaced) {
				if (p1Turn) {
					String clickedCell = e.getActionCommand();
					if (startPointPlacement) {
						placeStart(clickedCell);
					} else {
						// Place ship for player 1
						List<String> alteredEndpoints = convertEndpoints(endpoints);

						if (alteredEndpoints.contains(clickedCell)) {
							String ship = "";
							bModel.placeShip(p1Turn, currentStart, clickedCell,
									shipCount);
							bView.dehighlightEndpoints(p1Turn, endpoints);
							startPointPlacement = true;
							if (shipCount == MAX_SHIP_INDEX) {
								p1Turn = false;
								shipCount = 0;
								bView.enableGrid(false);
								bView.disableGrid(true);
								bView.updateLabel("Player 2, Place your aircraft "
										+ "carrier!");
							} else {
								ship = bModel
										.getShipName(p1Turn, shipCount + 1);
								bView.updateLabel("Player 1, Place your "
										+ ship + "!");
								shipCount++;
							}
						} else {
							bView.updateLabel("Please choose a valid highlighted "
									+ "endpoint!");
						}
					}
				} else {
					// Place ship for player 2
					String clickedCell = e.getActionCommand();
					if (startPointPlacement) {
						placeStart(clickedCell);
					} else {
						// Place ship for player 2
						List<String> alteredEndpoints = convertEndpoints(endpoints);

						if (alteredEndpoints.contains(clickedCell)) {
							String ship = "";
							bModel.placeShip(p1Turn, currentStart, clickedCell,
									shipCount);
							bView.dehighlightEndpoints(p1Turn, endpoints);
							startPointPlacement = true;
							if (shipCount == MAX_SHIP_INDEX) {
								shipsPlaced = true;
								p1Turn = true;
								bView.enableGrid(false);
								bView.disableGrid(true);
								bView.updateLabel("Player 1, Attack!");
							} else {
								ship = bModel
										.getShipName(p1Turn, shipCount + 1);
								bView.updateLabel("Player 2, Place your "
										+ ship + "!");
								shipCount++;
							}
						} else {
							bView.updateLabel("Please choose a valid highlighted "
									+ "endpoint!");
						}
					}
				}
			}
		}

	}

	/**
	 * Places the clicked cell as the starting point for the current ship and
	 * highlights the valid end points in the view.
	 * 
	 * @param clickedCell
	 *            Holds the location of the cell that has been clicked.
	 * @alters bView
	 * @ensures The start point and highlighted end points have not been
	 *          previously used.
	 */
	private void placeStart(String clickedCell) {
		endpoints = bModel.getEndpoints(p1Turn, clickedCell, shipCount);
		if (bModel.isValidPlacement(p1Turn, clickedCell)
				&& endpoints.size() != 0) {
			bView.highlightEndpoints(p1Turn, endpoints);
			currentStart = clickedCell;
			startPointPlacement = false;
		} else {
			bView.updateLabel("The selected cell is invalid, please choose another " 
					+ "cell.");
		}
	}

	/**
	 * Converts the numerical coordinates of each end point in the list from
	 * <int,int> format to <char,int>.
	 * 
	 * @param endpoints
	 *            Holds a set of valid end points for the chosen starting point.
	 * @ensures The returned array contains all endpoints converted to <char,
	 *          int>.
	 * @return Returns an array of <char, int> coordinates representing valid
	 *         end points.
	 * 
	 */
	private List<String> convertEndpoints(List<String> endpoints) {
		List<String> alteredPts = new ArrayList<String>();
		for (String s : endpoints) {
			int row = 0;
			char rowChar = ' ';
			String newEndpoint = "";
			if (s.charAt(1) == '0') {
				rowChar = 'J';
				newEndpoint = Character.toString(rowChar) + s.substring(2);
			} else {
				row = s.charAt(0) + 16;
				rowChar = (char) row;
				newEndpoint = Character.toString(rowChar) + s.substring(1);
			}
			alteredPts.add(newEndpoint);
		}
		return alteredPts;
	}

	/**
	 * Changes the boolean in order to indicate whose turn it is and disable the
	 * opposite players grid.
	 * 
	 * @alters bView
	 * @ensures p1Turn == !(#p1Turn)
	 */
	private void changeTurns() {
		if (p1Turn) {
			p1Turn = false;
		} else {
			p1Turn = true;
		}
		bView.enableGrid(!p1Turn);
		bView.disableGrid(p1Turn);
	}

}
